package main

/**
协程（Goroutine）
Go 语言中没有线程的概念，只有协程，也称为 goroutine。相比线程来说，协程更加轻量，一个程序可以随意启动成千上万个 goroutine。
goroutine 被 Go runtime 所调度，这一点和线程不一样。也就是说，Go 语言的并发是由 Go 自己所调度的，自己决定同时执行多少个 goroutine，
什么时候执行哪几个。这些对于我们开发者来说完全透明，只需要在编码的时候告诉 Go 语言要启动几个 goroutine，至于如何调度执行，我们不用关心。
要启动一个 goroutine 非常简单，Go 语言为我们提供了 go 关键字，相比其他编程语言简化了很多，如下面的代码所示：
*/

//func main() {
//	go fmt.Println("协程执行了！")
//	fmt.Println("main主协程")
//	time.Sleep(time.Second)  // 这里如果不让主协程多等待一秒，就看不到上面协程打印的信息了，因为主协程会在代码执行完毕后退出
//}
/**
从输出结果也可以看出，程序是并发的，go 关键字启动的 goroutine 并不阻塞 main goroutine 的执行，所以我们才会看到如上打印结果。
小提示：示例中的 time.Sleep(time.Second) 表示等待一秒，这里是让 main goroutine 等一秒，
不然 main goroutine 执行完毕程序就退出了，也就看不到启动的新 goroutine 中“飞雪无情”的打印结果了。
*/

// chan负责多个协程之间的通信

// 如何声明一个channel
//ch:= make(chan string)  // 其中 chan 是一个关键字，表示是 channel 类型。后面的 string 表示 channel 里的数据是 string 类型。通过 channel 的声明也可以看到，chan 是一个集合类型。
/**
定义好 chan 后就可以使用了，一个 chan 的操作只有两种：发送和接收。
接收：从 chan 中获取的值，操作符为 <- chan。
发送：向 chan 发送值，把值放在 chan 中，操作符为 chan <-。
*/
//func main() {
//	ch := make(chan string)
//	go func() {
//		fmt.Println("子协程启动！")
//		// 像ch中写入一个字符a
//		ch<- "a"
//	}()
//	fmt.Println("main 协程启动！")
//	// 接受channel中的值,如果channel中没有值，则这里的协程会一直处于等待状态
//	out := <-ch
//	fmt.Println("接受channel中的值：",out)
//}
// 无缓冲 channel
/**
上面的示例中，使用 make 创建的 chan 就是一个无缓冲 channel，
它的容量是 0，不能存储任何数据。所以无缓冲 channel 只起到传输数据的作用，数据并不会在 channel 中做任何停留。
这也意味着，无缓冲 channel 的发送和接收操作是同时进行的，它也可以称为同步 channel。
*/
//有缓冲 channel
/**
有缓冲 channel 类似一个可阻塞的队列，内部的元素先进先出。通过 make 函数的第二个参数可以指定 channel 容量的大小，进而创建一个有缓冲 channel，如下面的代码所示：
*/
//func main() {
//	cacheCh := make(chan int,5) // 这是一个channel，存放的元素类型为int，容量为5
//	// 我创建了一个容量为 5 的 channel，内部的元素类型是 int，也就是说这个 channel 内部最多可以存放 5 个类型为 int 的元素：
//	/**
//	一个有缓冲 channel 具备以下特点：
//	有缓冲 channel 的内部有一个缓冲队列；
//	发送操作是向队列的尾部插入元素，如果队列已满，则阻塞等待，直到另一个 goroutine 执行，接收操作释放队列的空间；
//	接收操作是从队列的头部获取元素并把它从队列中删除，如果队列为空，则阻塞等待，直到另一个 goroutine 执行，发送操作插入新的元素。
//	因为有缓冲 channel 类似一个队列，可以获取它的容量和里面元素的个数。如下面的代码所示：
//	*/
//	cacheCh <- 1
//	cacheCh <- 2
//	cacheCh <- 3
//	fmt.Println("cache的容量为：",cap(cacheCh),"cache的元素个数为：",len(cacheCh))
//	// 无缓冲 channel 其实就是一个容量大小为 0 的 channel。比如 make(chan int,0)。
//
//	// 关闭channel
//	//close(cacheCh)
//	// 如果一个 channel 被关闭了，就不能向里面发送数据了，如果发送的话，会引起 painc 异常。但是还可以接收 channel 里的数据，如果 channel 里没有数据的话，接收的数据是元素类型的零值。
//
//	// 单向chan
//	/**
//	有时候，我们有一些特殊的业务需求，比如限制一个 channel 只可以接收但是不能发送，或者限制一个 channel 只能发送但不能接收，这种 channel 称为单向 channel。
//	单向 channel 的声明也很简单，只需要在声明的时候带上 <- 操作符即可，如下面的代码所示：
//	 */
//	//OnlySendToChan := make(chan <- int)
//	//OnlyReceiveFromChan := make(<-chan int)
//	// 在函数或者方法的参数中，使用单向 channel 的较多，这样可以防止一些操作影响了 channel。
//
//}

/**
下面示例中的 counter 函数，它的参数 out 是一个只能发送的 channel，所以在 counter 函数体内使用参数 out 时，只能对其进行发送操作，如果执行接收操作，则程序不能编译通过。
*/
func counter(out chan<- int) {
	//函数内容使用变量out，只能进行发送操作
}
